# -*- coding: utf-8 -*-

import os
import sys
import re
import json
import logging
import concurrent.futures
import time
from typing import List, Dict, Any, Optional
os.environ['CUDA_VISIBLE_DEVICES'] = '5'

# 添加OpenAI API所需的导入
import openai
# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# 现在导入其他必要的库
import torch
import torch.nn.functional as F
from generate_reply_prompt import GenerateReplyPrompt

# 添加ModelScope相关的导入
from modelscope import AutoModelForCausalLM, AutoTokenizer
from transformers.generation.utils import GenerationConfig

class ContrastiveTokenDecoder:
    def __init__(self, model, tokenizer, config, device=None):
        """
        使用同一模型的两个前向流（persona_prompt vs neutral_prompt）进行逐token对比解码
        """
        self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
        self.tokenizer = tokenizer
        self.model = model
        self.config = config
        # 从配置中获取对比解码参数，如果没有则使用默认值
        self.alpha = getattr(config, 'CONTRASTIVE_ALPHA', 0.3)
        self.top_k = getattr(config, 'CONTRASTIVE_TOP_K', 40)
        self.temperature = getattr(config, 'CONTRASTIVE_TEMPERATURE', 0.7)
        
        # 确保tokenizer有pad_token
        if self.tokenizer.pad_token is None:
            self.tokenizer.pad_token = self.tokenizer.eos_token
            
    @torch.no_grad()
    def generate(self, persona_prompt, neutral_prompt, max_new_tokens=1024):
        persona_ids = self.tokenizer(persona_prompt, return_tensors="pt").to(self.device).input_ids
        neutral_ids = self.tokenizer(neutral_prompt, return_tensors="pt").to(self.device).input_ids
        generated_ids = persona_ids.clone()

        for _ in range(max_new_tokens):
            # 每步 forward 用完整序列
            out_main = self.model(input_ids=generated_ids)
            out_ref = self.model(input_ids=neutral_ids)

            logits_main = out_main.logits[:, -1, :]  # [batch, vocab_size]
            logits_ref = out_ref.logits[:, -1, :]    # [batch, vocab_size]

            contrastive_logits = logits_main - self.alpha * logits_ref

            top_k_vals, top_k_idx = torch.topk(contrastive_logits, self.top_k)
            probs = F.softmax(top_k_vals / self.temperature, dim=-1)
            next_token = top_k_idx[0, torch.multinomial(probs[0], 1)]

            generated_ids = torch.cat([generated_ids, next_token.unsqueeze(0)], dim=-1)

            if next_token.item() == self.tokenizer.eos_token_id:
                break

        return self.tokenizer.decode(generated_ids[0], skip_special_tokens=True)
class StrongWeakContrastiveDecoder:
    def __init__(self, strong_model, weak_model, tokenizer, config, device=None):
        """
        使用强弱两个不同模型进行逐token对比解码
        """
        self.device = device or ("cuda" if torch.cuda.is_available() else "cpu")
        self.tokenizer = tokenizer
        self.strong_model = strong_model  # 强模型（通常是较大的模型）
        self.weak_model = weak_model  # 弱模型（通常是较小的模型）
        self.config = config
        # 从配置中获取对比解码参数，如果没有则使用默认值
        self.alpha = getattr(config, 'STRONG_WEAK_ALPHA', 0.5)
        self.top_k = getattr(config, 'CONTRASTIVE_TOP_K', 40)
        self.temperature = getattr(config, 'CONTRASTIVE_TEMPERATURE', 0.7)
        
        # 确保tokenizer有pad_token
        if self.tokenizer.pad_token is None:
            self.tokenizer.pad_token = self.tokenizer.eos_token
            
    @torch.no_grad()
    def generate(self, prompt, max_new_tokens=1024):
        """
        使用强弱模型进行对比解码生成回答
        
        Args:
            prompt: 提示词文本
            max_new_tokens: 最大生成token数
            
        Returns:
            生成的回答文本
        """
        input_ids = self.tokenizer(prompt, return_tensors="pt").to(self.device).input_ids
        generated_ids = input_ids.clone()
        
        for _ in range(max_new_tokens):
            # 每步 forward 用完整序列
            out_strong = self.strong_model(input_ids=generated_ids)
            out_weak = self.weak_model(input_ids=generated_ids)
            
            logits_strong = out_strong.logits[:, -1, :]  # [batch, vocab_size]
            logits_weak = out_weak.logits[:, -1, :]    # [batch, vocab_size]
            
            # 检查并处理词汇表维度不匹配问题
            # 虽然分词器词汇表大小相同，但模型嵌入层大小可能不同
            if logits_strong.shape[1] != logits_weak.shape[1]:
                logger.warning(f"词汇表维度不匹配: 强模型={logits_strong.shape[1]}, 弱模型={logits_weak.shape[1]}")
                # 获取最小维度并截断较大的张量
                min_dim = min(logits_strong.shape[1], logits_weak.shape[1])
                if logits_strong.shape[1] > min_dim:
                    logits_strong = logits_strong[:, :min_dim]
                if logits_weak.shape[1] > min_dim:
                    logits_weak = logits_weak[:, :min_dim]
            
            # 对比解码：强模型的logits减去弱模型logits的加权版本
            contrastive_logits = logits_strong - self.alpha * logits_weak
            
            top_k_vals, top_k_idx = torch.topk(contrastive_logits, self.top_k)
            probs = F.softmax(top_k_vals / self.temperature, dim=-1)
            next_token = top_k_idx[0, torch.multinomial(probs[0], 1)]
            
            generated_ids = torch.cat([generated_ids, next_token.unsqueeze(0)], dim=-1)
            
            if next_token.item() == self.tokenizer.eos_token_id:
                break
            
        return self.tokenizer.decode(generated_ids[0], skip_special_tokens=True)

class ModelHandler:
    """
    模型处理器，负责加载模型和生成回答
    """
    
    def __init__(self, config):
        """
        初始化模型处理器
        
        Args:
            config: 配置对象
        """
        self.config = config
        self.tokenizer = None
        self.model = None  # 强模型（默认模型）
        self.weak_model = None  # 弱模型
        self.openai_client = None
        
        # 设置设备（直接使用config.DEVICE）
        self.device = self._setup_device()
        
        # 初始化提示词生成器
        self.prompt_generator = GenerateReplyPrompt("/mnt/ssd/jsj/patient/script/人格定义.json")
        
        # 初始化对比解码器
        self.contrastive_decoder = None
        self.strong_weak_decoder = None  # 强弱模型对比解码器
        
        # 初始化模块化方法
        self.modular_generator = None
        if getattr(self.config, 'USE_MODULAR_APPROACH', False):
            self._init_modular_generator()
        
        # 初始化交互矩阵方法
        self.interaction_matrix = None
        if getattr(self.config, 'USE_INTERACTION_MATRIX', False):
            self._init_interaction_matrix()
        
        # 可选：初始化 Swift 引擎
        self.swift_engine = None
        if getattr(self.config, 'USE_SWIFT_ENGINE', False):
            self._init_swift_engine()
        
        # 如果使用API模型，初始化OpenAI客户端
        if getattr(self.config, 'USE_API_MODEL', False):
            self._init_openai_client()
    
    def _init_modular_generator(self):
        """
        初始化模块化生成器
        """
        try:
            # 导入模块化组件（将脚本根目录加入sys.path，作为包导入 method）
            import sys
            import os
            script_root = os.path.dirname(__file__)
            if script_root not in sys.path:
                sys.path.append(script_root)
            
            from method.modular_integration import ModularResponseGenerator
            
            # 创建模块化生成器
            self.modular_generator = ModularResponseGenerator(self, self.config)
            logger.info("模块化生成器初始化成功")
            
        except Exception as e:
            logger.error(f"模块化生成器初始化失败: {str(e)}")
            self.modular_generator = None
        
    def _init_openai_client(self):
        """
        初始化OpenAI客户端
        """
        try:
            api_key = getattr(self.config, 'API_KEY', '')
            base_url = getattr(self.config, 'API_BASE_URL', 'https://api.chatanywhere.tech/v1')
            
            if not api_key or api_key == 'your_api_key_here':
                logger.warning("API密钥未设置或使用了默认值，请在config.py中配置有效的API密钥")
                
            self.openai_client = openai.OpenAI(
                api_key=api_key,
                base_url=base_url
            )
            logger.info(f"OpenAI客户端初始化成功，使用基础URL: {base_url}")
        except Exception as e:
            logger.error(f"OpenAI客户端初始化失败: {str(e)}")
            self.openai_client = None

    def _init_interaction_matrix(self):
        """
        初始化交互矩阵
        """
        try:
            import sys
            import os
            # 添加交互矩阵模块路径
            matrix_path = getattr(self.config, 'INTERACTION_MATRIX_PATH', '')
            if matrix_path and os.path.exists(matrix_path):
                matrix_dir = os.path.dirname(matrix_path)
                if matrix_dir not in sys.path:
                    sys.path.append(matrix_dir)
                
                from interaction_matrix import get_complete_interaction_matrix
                self.interaction_matrix = get_complete_interaction_matrix()
                logger.info("交互矩阵初始化成功")
            else:
                logger.warning(f"交互矩阵文件不存在: {matrix_path}")
                self.interaction_matrix = None
        except Exception as e:
            logger.error(f"交互矩阵初始化失败: {str(e)}")
            self.interaction_matrix = None

    def _init_swift_engine(self):
        """
        初始化 Swift LLM 引擎（可选路径）
        """
        try:
            from swift.llm import PtEngine
            model_path = getattr(self.config, 'SWIFT_MODEL_PATH', '')
            # 移除use_flash_attention_2参数，因为Qwen2ForCausalLM模型不支持这个参数
            self.swift_engine = PtEngine(model_path, max_batch_size=2)
            logger.info(f"Swift 引擎初始化成功: {model_path}")
        except Exception as e:
            logger.error(f"Swift 引擎初始化失败: {str(e)}")
            self.swift_engine = None

    def _call_swift_generate(self, model_input: List[Dict[str, str]]) -> str:
        """
        使用 Swift 引擎生成单个回答
        """
        try:
            if self.swift_engine is None:
                raise RuntimeError("Swift 引擎未初始化")
            from swift.llm import RequestConfig, InferRequest
            max_tokens = getattr(self.config, 'SWIFT_MAX_TOKENS', 1024)
            temperature = getattr(self.config, 'SWIFT_TEMPERATURE', 0.7)
            req_cfg = RequestConfig(max_tokens=max_tokens, temperature=temperature)
            infer_req = InferRequest(messages=model_input)
            resp_list = self.swift_engine.infer([infer_req], req_cfg)
            content = resp_list[0].choices[0].message.content
            return self.strip_think_tags(content)
        except Exception as e:
            logger.error(f"Swift 生成失败: {str(e)}")
            return "（生成回答时发生错误）"

    def generate_with_messages(self, messages: List[Dict[str, str]]) -> str:
        """
        统一的消息生成入口：根据配置选择 Swift 或本地/API
        """
        try:
            if getattr(self.config, 'USE_SWIFT_ENGINE', False) and self.swift_engine is not None:
                return self._call_swift_generate(messages)
            return self._call_model_generate(messages)
        except Exception as e:
            logger.error(f"generate_with_messages 调用失败: {str(e)}")
            return "（生成回答时发生错误）"
    
    def _setup_device(self):
        """
        直接使用config.DEVICE，不再改CUDA_VISIBLE_DEVICES
        
        Returns:
            torch.device对象
        """
        device_str = getattr(self.config, "DEVICE", "cpu")
        device = torch.device(device_str)
        logger.info(f"使用设备: {device}")
        return device

    def load_model(self, model_path: str) -> None:
        """
        加载模型
        
        Args:
            model_path: 模型路径或模型ID
        """
        # 如果使用API模型，不需要加载本地模型
        if getattr(self.config, 'USE_API_MODEL', False):
            logger.info("使用API模型，跳过本地模型加载")
            return
        
        try:
            logger.info(f"当前使用的设备: {self.device}")
            logger.info(f"可用GPU数量: {torch.cuda.device_count() if torch.cuda.is_available() else 0}")
            
            # 检查是否启用了强弱模型对比解码
            use_strong_weak_contrastive = getattr(self.config, 'USE_STRONG_WEAK_CONTRASTIVE', False)
            
            # 确定强模型路径
            if use_strong_weak_contrastive:
                strong_model_path = getattr(self.config, 'STRONG_MODEL_PATH', '')
                if strong_model_path:
                    logger.info(f"使用配置中的强模型路径: {strong_model_path}")
                    self.tokenizer = self._load_tokenizer(strong_model_path)
                    self.model = self._load_model_instance(strong_model_path)
                else:
                    logger.warning("未配置强模型路径，使用默认模型路径")
                    logger.info(f"开始加载ModelScope模型: {model_path}")
                    self.tokenizer = self._load_tokenizer(model_path)
                    self.model = self._load_model_instance(model_path)
            else:
                logger.info(f"开始加载ModelScope模型: {model_path}")
                self.tokenizer = self._load_tokenizer(model_path)
                self.model = self._load_model_instance(model_path)
            
            # 设置生成配置
            self._setup_generation_config(model_path)
            
            # 初始化对比解码器
            self.contrastive_decoder = ContrastiveTokenDecoder(
                model=self.model,
                tokenizer=self.tokenizer,
                config=self.config,
                device=self.device
            )
            
            # 如果启用了强弱模型对比解码，加载弱模型
            use_strong_weak_contrastive = getattr(self.config, 'USE_STRONG_WEAK_CONTRASTIVE', False)
            if use_strong_weak_contrastive:
                weak_model_path = getattr(self.config, 'WEAK_MODEL_PATH', '')
                if weak_model_path and weak_model_path != model_path:
                    try:
                        logger.info(f"开始加载弱模型: {weak_model_path}")
                        # 加载弱模型
                        self.weak_model = self._load_model_instance(weak_model_path)
                        
                        # 初始化强弱模型对比解码器
                        self.strong_weak_decoder = StrongWeakContrastiveDecoder(
                            strong_model=self.model,
                            weak_model=self.weak_model,
                            tokenizer=self.tokenizer,
                            config=self.config,
                            device=self.device
                        )
                        logger.info("强弱模型对比解码器初始化成功")
                    except Exception as e:
                        logger.error(f"加载弱模型或初始化强弱模型对比解码器失败: {str(e)}")
                        self.weak_model = None
                        self.strong_weak_decoder = None
            
            logger.info("模型加载成功")
            
        except Exception as e:
            logger.error(f"模型加载失败: {str(e)}")
            raise
    
    def _load_tokenizer(self, model_path: str) -> AutoTokenizer:
        """
        加载模型分词器
        
        Args:
            model_path: 模型路径或模型ID
        
        Returns:
            加载好的分词器
        """
        tokenizer = AutoTokenizer.from_pretrained(
            model_path,
            # use_fast=False,
            trust_remote_code=True,
            # padding_side='left'
        )
        
        # 为没有chat_template的老模型（如Baichuan2-13B-Chat）手动设置chat_template
        if tokenizer.chat_template is None:
            tokenizer.chat_template = (
                "{% for message in messages %}"
                "{% if message['role'] == 'system' %}系统{{ message['content'] }}\n"  # 处理system消息
                "{% elif message['role'] == 'user' %}用户: {{ message['content'] }}\n"
                "{% elif message['role'] == 'assistant' %}助手: {{ message['content'] }}\n"
                "{% endif %}"
                "{% endfor %}"
                "助手:"
            )
            logger.info(f"为模型 {model_path} 手动设置了chat_template")
        
        return tokenizer
    
    def _load_model_instance(self, model_path: str) -> AutoModelForCausalLM:
        """
        加载模型实例
        
        Args:
            model_path: 模型路径或模型ID
        
        Returns:
            加载好的模型
        """
        if self.device.type == 'cuda':
            # 在GPU上加载模型
            model = AutoModelForCausalLM.from_pretrained(
                model_path,
                device_map="auto",
                torch_dtype=torch.bfloat16,
                trust_remote_code=True
            ) 
            logger.info(f"模型已加载到设备: {self.device}")
        return model

    def _setup_generation_config(self, model_path: str) -> None:
        """
        设置模型生成配置
        
        Args:
            model_path: 模型路径或模型ID
        """
        self.model.generation_config = GenerationConfig.from_pretrained(
            model_path
        )

    
    def _parse_personality_info(self, personality_info: Any) -> Dict[str, str]:
        """
        解析人格信息为字典格式
        
        Args:
            personality_info: 人格信息（可能是字符串、字典或None）
        
        Returns:
            解析后的人格信息字典
        """
        target_personality = {}
        
        try:
            if isinstance(personality_info, str):
                # 预处理：将所有换行符转换为逗号，以便统一处理
                processed_text = personality_info.replace('\n', ',').strip()
                
                # 尝试JSON解析
                if processed_text.startswith('{') and processed_text.endswith('}'):
                    target_personality = self._parse_json_personality(processed_text)
                else:
                    # 直接尝试宽松格式解析
                    target_personality = self._parse_loose_format_personality(processed_text)
            elif isinstance(personality_info, dict):
                target_personality = personality_info
                logger.debug(f"人格信息已是字典格式: {target_personality}")
            elif personality_info is None:
                logger.debug("人格信息为None")
            
            # 如果解析结果为空，设置默认值
            if not target_personality:
                logger.debug("解析结果为空，设置默认人格信息")
                target_personality = {"性格": "未知", "情绪状态": "未知"}
            
            logger.debug(f"最终人格信息解析结果: {target_personality}")
        except Exception as e:
            logger.warning(f"解析人格信息失败: {str(e)}")
            # 即使失败，也设置一个默认的人格信息以避免后续错误
            target_personality = {"性格": "未知", "情绪状态": "未知"}
        
        return target_personality
    
    def _parse_json_personality(self, processed_text: str) -> Dict[str, str]:
        """
        尝试JSON格式解析人格信息
        
        Args:
            processed_text: 处理后的文本
        
        Returns:
            解析后的人格信息字典
        """
        try:
            # 处理单引号JSON格式
            if '\'' in processed_text and '"' not in processed_text:
                processed_text = processed_text.replace('\'', '"')
            target_personality = json.loads(processed_text)
            logger.debug(f"JSON解析成功: {target_personality}")
            return target_personality
        except json.JSONDecodeError as e:
            # JSON解析失败，尝试宽松格式解析
            logger.info(f"JSON解析失败 ({str(e)}), 尝试宽松格式解析人格信息")
            # 更健壮的宽松格式解析
            # 先去掉首尾的大括号
            processed_text = processed_text[1:-1].strip()
            return self._parse_loose_format_personality(processed_text)
    
    def _parse_loose_format_personality(self, processed_text: str) -> Dict[str, str]:
        """
        尝试宽松格式解析人格信息
        
        Args:
            processed_text: 处理后的文本
        
        Returns:
            解析后的人格信息字典
        """
        target_personality = {}
        
        # 分割键值对（支持分号和逗号）
        if ';' in processed_text:
            pairs = [p.strip() for p in processed_text.split(';') if p.strip()]
        else:
            pairs = [p.strip() for p in processed_text.split(',') if p.strip()]
        
        for pair in pairs:
            if ':' in pair or '：' in pair:
                try:
                    key, value = pair.split(':', 1) if ':' in pair else pair.split('：', 1)
                    key = key.strip().strip('"\'')
                    value = value.strip().strip('"\'')
                    # 检查是否是我们关心的人格维度
                    if key in ["性格", "情绪状态", "病史回忆能力", "医学理解力", "语言表达能力", "沟通意愿"]:
                        target_personality[key] = value
                        logger.debug(f"宽松解析到 {key}: {value}")
                except ValueError:
                    # 分割失败，跳过这个键值对
                    logger.debug(f"无法解析键值对: {pair}")
        
        return target_personality
    
    def _parse_patient_info(self, basic_info: Any) -> Dict[str, str]:
        """
        解析患者基本信息
        
        Args:
            basic_info: 患者基本信息（可能是字符串、字典或None）
        
        Returns:
            解析后的患者基本信息字典
        """
        patient_info_dict = {}
        
        try:
            if isinstance(basic_info, str):
                # 简单解析患者基本信息
                for line in basic_info.split('\n'):
                    if ':' in line or '：' in line:
                        key, value = re.split(r'[:：]\s*', line.strip(), maxsplit=1)
                        patient_info_dict[key] = value
            elif isinstance(basic_info, dict):
                patient_info_dict = basic_info
        except Exception as e:
            logger.warning(f"解析患者基本信息失败: {str(e)}")
        
        return patient_info_dict
    
    
    def _convert_model_input_to_text(self, model_input: List[Dict[str, str]]) -> str:
        """
        使用tokenizer的apply_chat_template方法将模型输入列表转换为文本格式
        
        Args:
            model_input: 模型输入列表
        
        Returns:
            转换后的文本
        """
        try:
            # 使用官方推荐的tokenizer.apply_chat_template方法
            # 这符合模型期望的标准格式，包括添加生成提示
            text = self.tokenizer.apply_chat_template(
                model_input,
                tokenize=False,
                add_generation_prompt=True,
                #thinking_mode='off'
                enable_thinking=False
            )
            return text
        except Exception as e:
            logger.error(f"使用apply_chat_template失败: {str(e)}")
            # 回退到手动拼接方式
            prompt_text = ""
            
            # 提取system消息
            system_message = next((msg for msg in model_input if msg.get('role') == 'system'), None)
            if system_message:
                prompt_text += system_message.get('content', '') + '\n'
            
            # 添加当前问题
            last_message = model_input[-1] if model_input else None
            if last_message and last_message.get('role') == 'user':
                prompt_text += f"用户: {last_message.get('content', '')}\n助手:"
            
            return prompt_text

    def strip_think_tags(self,text: str) -> str:
        """
        删除<think>...</think>部分，只保留其后的模型输出内容
        """
        # 用正则匹配并替换
       # cleaned = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
        #cleaned = re.sub(r"^.*?</think>", "", text, flags=re.DOTALL)
        cleaned = re.sub(r"^.*?</think>", "", text, flags=re.DOTALL)
        return cleaned.strip()
    
    def _call_model_generate(self, model_input: List[Dict[str, str]]) -> str:
        """
        调用模型生成单个回答
        
        Args:
            model_input: 模型输入列表
            
        Returns:
            生成的回答文本
        """
        try:
            # 检查是否使用API模型
            if getattr(self.config, 'USE_API_MODEL', False) and self.openai_client is not None:
                # 使用API模型生成
                return self._call_api_generate(model_input)
                
            merged_params = {
                "max_new_tokens": 2048,  # 降低最大新生成token数，防止生成过长内容
                "temperature": 0.7,
                "top_p": 0.9,
                "top_k": 20,
                "repetition_penalty": 1.1,
                "max_input_length": 32768
            }
            
            # 转换model_input为文本格式
            prompt_text = self._convert_model_input_to_text(model_input)
            #logger.info(f"转换后的提示词: {prompt_text}")
            
            # 使用torch.no_grad()优化内存使用
            with torch.no_grad():
                # 检查是否使用强弱模型对比解码
                use_strong_weak_contrastive = getattr(self.config, 'USE_STRONG_WEAK_CONTRASTIVE', False)
                if use_strong_weak_contrastive and self.strong_weak_decoder is not None:
                    # 使用强弱模型对比解码
                    try:
                        logger.info("使用强弱模型对比解码生成回答")
                        generated_text = self.strong_weak_decoder.generate(
                            prompt=prompt_text,
                            max_new_tokens=2048
                        )
                        
                        # 处理生成结果
                        generated_text = self.strip_think_tags(generated_text)
                        return generated_text
                    except Exception as e:
                        logger.warning(f"强弱模型对比解码执行失败，回退到普通生成: {str(e)}")
                
                # 检查是否使用对比解码（默认使用）
                use_contrastive_decoding = getattr(self.config, 'USE_CONTRASTIVE_DECODING', True)
                
                if use_contrastive_decoding and self.contrastive_decoder is not None:
                    # 使用对比解码
                    try:
                        # 查找是否包含完整的人格信息
                        system_message = next((msg for msg in model_input if msg.get('role') == 'system'), None)
                        if system_message:
                            # 直接构建简化的中性提示词
                            system_content = system_message.get('content', '')
                            
                            # 创建中性版本的system消息
                            # 按照用户要求的格式构建
                            neutral_system_content = "任务：你是一名患者，根据对话历史和患者信息，与医生进行门诊对话。\n"
                            
                            # 添加患者基本信息（从原始提示词中提取）
                            if '患者基本信息：' in system_content:
                                patient_info_start = system_content.find('患者基本信息：')
                                patient_info_end = system_content.find('\n\n', patient_info_start)
                                if patient_info_end != -1:
                                    patient_info = system_content[patient_info_start:patient_info_end]
                                    neutral_system_content += f"\n{patient_info}\n"
                            
                            # # 添加简单的人格键值对（不包含详细定义）
                            # import re
                            
                            # # 过滤出需要的人格维度
                            # personality_dimensions = {
                            #     '性格': '',
                            #     '情绪状态': '',
                            #     '病史回忆能力': '',
                            #     '医学理解力': '',
                            #     '语言表达能力': ''
                            # }
                            
                            # # 尝试从"患者人格特征详细定义："部分提取人格信息
                            # if '患者人格特征详细定义：' in system_content:
                            #     # 定位到人格特征详细定义部分
                            #     personality_start = system_content.find('患者人格特征详细定义：')
                                
                            #     # 查找人格信息部分的结束位置（下一个主要部分的开始）
                            #     personality_end = system_content.find('\n\n对话历史：', personality_start)
                            #     if personality_end == -1:
                            #         personality_end = system_content.find('\n\n生成要求：', personality_start)
                            #         if personality_end == -1:
                            #             # 如果找不到结束标记，就取到文件末尾
                            #             personality_end = len(system_content)
                                
                            #     # 提取人格信息部分
                            #     personality_section = system_content[personality_start:personality_end]
                                
                            #     # 针对每种人格维度分别提取
                            #     # 性格
                            #     personality_match = re.search(r'性格\s*\(.*?\):\s*(\S+)', personality_section)
                            #     if personality_match:
                            #         personality_dimensions['性格'] = personality_match.group(1)
                                
                            #     # 情绪状态
                            #     emotion_match = re.search(r'情绪状态\s*\(.*?\):\s*(\S+)', personality_section)
                            #     if emotion_match:
                            #         personality_dimensions['情绪状态'] = emotion_match.group(1)
                                
                            #     # 病史回忆能力
                            #     recall_match = re.search(r'病史回忆能力\s*\(.*?\):\s*(\S+)', personality_section)
                            #     if recall_match:
                            #         personality_dimensions['病史回忆能力'] = recall_match.group(1)
                                
                            #     # 医学理解力
                            #     literacy_match = re.search(r'医学理解力\s*\(.*?\):\s*(\S+)', personality_section)
                            #     if literacy_match:
                            #         personality_dimensions['医学理解力'] = literacy_match.group(1)
                                
                            #     # 语言表达能力
                            #     language_match = re.search(r'语言表达能力\s*\(.*?\):\s*(\S+)', personality_section)
                            #     if language_match:
                            #         personality_dimensions['语言表达能力'] = language_match.group(1)
                            
                            # # 添加简化的人格描述
                            # neutral_system_content += "\n患者人格特征：\n"
                            # for dim_name, dim_value in personality_dimensions.items():
                            #     if dim_value:
                            #         neutral_system_content += f"- {dim_name}：{dim_value}\n"
                            
                            # 添加对话历史（从原始提示词中提取）
                            if '对话历史：' in system_content:
                                history_start = system_content.find('对话历史：')
                                history_end = system_content.find('\n\n生成要求：', history_start)
                                if history_end != -1:
                                    history_content = system_content[history_start:history_end]
                                    neutral_system_content += f"\n{history_content}\n"
                            
                            # 添加生成要求
                            neutral_system_content += "\n\n生成要求：\n"
                            neutral_system_content += "-  仅扮演患者，与医生自然对话。。\n"
                            neutral_system_content += "- 回答内容应基于病情逻辑合理，不编造新疾病\n"
                            neutral_system_content += "- 保持普通患者的日常口语表达，回答简洁自然\n"
                            neutral_system_content += "- 【输出要求】：仅生成最终患者的回应内容，绝对不包含本system指令中的任何内容\n"
                            neutral_system_content += "- 【格式要求】：直接输出患者的回应内容，不要添加任何前缀或标识（如'患者：'等）\n"
                                    
                            # 创建中性提示词
                            neutral_model_input = []
                            neutral_system_msg = {"role": "system", "content": neutral_system_content}
                            neutral_model_input.append(neutral_system_msg)
                            
                            # 添加用户消息
                            user_message = next((msg for msg in model_input if msg.get('role') == 'user'), None)
                            if user_message:
                                neutral_model_input.append(user_message)
                                
                            logger.info(f"对比解码中性提示词: {neutral_model_input}")

                            # 转换为文本格式
                            neutral_prompt_text = self._convert_model_input_to_text(neutral_model_input)
                            
                            # 使用对比解码生成回答
                            logger.info("使用对比解码生成回答")
                            generated_text = self.contrastive_decoder.generate(
                                persona_prompt=prompt_text,
                                neutral_prompt=neutral_prompt_text,
                                max_new_tokens=2048
                            )
                            
                            # 处理生成结果
                            generated_text = self.strip_think_tags(generated_text)
                            return generated_text
                    except Exception as e:
                        logger.warning(f"对比解码执行失败，回退到普通生成: {str(e)}")
                
                # 普通生成方式
                # 对输入进行tokenize处理
                inputs = self.tokenizer(
                    prompt_text,
                    return_tensors="pt"
                ).to(self.device)

                #baichuan
                generated_ids = self.model.generate(
                    **inputs,
                    max_new_tokens=4096
                )
                output_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(inputs.input_ids, generated_ids)][0].tolist()
                # 5. parsing thinking content
                try:
                    # rindex finding 151668 (</think>)
                    index = len(output_ids) - output_ids[::-1].index(151668)
                except ValueError:
                    index = 0

                thinking_content = self.tokenizer.decode(output_ids[:index], skip_special_tokens=True).strip("\n")
                content = self.tokenizer.decode(output_ids[index:], skip_special_tokens=True).strip("\n")  
                logger.info(f"内容: {content}")
                content= self.strip_think_tags(content)
                logger.info(f"思考内容: {thinking_content}")
                return content
                
        except Exception as e:
            logger.error(f"生成回答失败: {str(e)}")
            return "（生成回答时发生错误）"
    

    def _call_api_generate(self, model_input: List[Dict[str, str]]) -> str:
        """
        调用OpenAI API生成回答
        
        Args:
            model_input: 模型输入列表
            
        Returns:
            生成的回答文本
        """
        try:
            # 获取API模型名称
            model_name = getattr(self.config, 'API_MODEL_NAME', 'gpt-4o-mini')
            
            # 调用API生成回答
            logger.info(f"调用OpenAI API生成回答，模型: {model_name}")
            response = self.openai_client.chat.completions.create(
                model=model_name,
                messages=model_input,
                temperature=0.7,
                max_tokens=1024,
                top_p=0.9,
                frequency_penalty=0,
                presence_penalty=0
            )
            
            # 提取生成的内容
            generated_content = response.choices[0].message.content.strip()
            logger.info(f"API生成内容: {generated_content}")
            
            # 应用相同的后处理
            cleaned_content = self.strip_think_tags(generated_content)
            return cleaned_content
            
        except Exception as e:
            logger.error(f"调用OpenAI API失败: {str(e)}")
            return "（调用API生成回答时发生错误）"
    
    def batch_generate_responses(
        self,
        batch_data: List[Dict[str, Any]]
    ) -> List[str]:
        """
        批量生成回答
        
        Args:
            batch_data: 批量数据
        
        Returns:
            生成的回答列表
        """
        start_time = time.time()
        responses = []
        
        # 检查是否使用模块化方法
        use_modular = getattr(self.config, 'USE_MODULAR_APPROACH', False)
        if use_modular and self.modular_generator is not None:
            logger.info("使用模块化方法生成回答")
            try:
                responses = self.modular_generator.generate_batch_responses(batch_data)
                logger.info(f"模块化批量处理完成，总耗时: {time.time() - start_time:.2f}秒")
                return responses
            except Exception as e:
                logger.error(f"模块化方法生成失败，回退到传统方法: {str(e)}")
                # 回退到传统方法
                pass
        
        # 检查是否使用交互矩阵方法
        use_interaction_matrix = getattr(self.config, 'USE_INTERACTION_MATRIX', False)
        if use_interaction_matrix and self.interaction_matrix is not None:
            logger.info("使用交互矩阵方法生成回答")
            try:
                responses = self._generate_with_interaction_matrix(batch_data)
                logger.info(f"交互矩阵批量处理完成，总耗时: {time.time() - start_time:.2f}秒")
                return responses
            except Exception as e:
                logger.error(f"交互矩阵方法生成失败，回退到传统方法: {str(e)}")
                # 回退到传统方法
                pass
        
        # 传统方法：检查模型是否已加载（使用API模型时跳过此检查）
        use_api_model = getattr(self.config, 'USE_API_MODEL', False)
        if not use_api_model and (self.tokenizer is None or self.model is None):
            logger.error("模型或分词器未加载，无法生成回答")
            return ["（模型未初始化，无法生成回答）"] * len(batch_data)
        
        # 使用API模型时，额外检查OpenAI客户端是否已初始化
        if use_api_model and self.openai_client is None:
            logger.error("OpenAI客户端未初始化，无法调用API生成回答")
            return ["（OpenAI客户端未初始化，无法生成回答）"] * len(batch_data)
        
        # 直接使用串行处理每个病人的请求，确保回答不会混淆
        try:
            logger.info(f"使用串行方式处理批量请求，总数量: {len(batch_data)}")
            
            for i, data in enumerate(batch_data):
                logger.info(f"处理第 {i+1}/{len(batch_data)} 个请求")
                
                # 检查医生问题是否为空
                if not data.get("doctor_question") or not data["doctor_question"].strip():
                    logger.warning("医生问题为空，将使用默认问题")
                    data["doctor_question"] = "请描述你的症状和感受"
                
                # 解析人格信息
                target_personality = self._parse_personality_info(data["人格"])

                # 解析患者基本信息
                patient_info_dict = self._parse_patient_info(data["基础信息"])

                # 生成详细的system内容，包含思维链引导+人格定义匹配  （仅作为你的推理过程，不体现在最终回应中）
                system_content = "任务：请扮演一个具有特定人格特征的患者，与医生进行门诊对话。\n\n"#在生成回应时，必须按规定步骤一步步思考并给出思考结果：
                # 新增：思维链推理步骤（完全结合人格定义的5个维度）
                # system_content += "【必须完成的5步思考步骤】\n"
                # system_content += "1. 理解医生意图：明确医生当前问题的核心（例如：医生问‘你咳嗽多久了’，是想了解症状持续时间，用于判断病情阶段）\n"
                # system_content += "2. 提取相关事实：从【患者基本信息】【门诊病历】中提取相关事实（例如：病历写‘咳嗽5天’，则需基于此回应，不编造‘咳嗽10天’）\n"
                # system_content += "3. 总结历史对话：从患者的历史对话里总结出患者提到的病情和患者的语言风格\n"
                # system_content += "4. 生成中性回答：根据患者事实、总结病情、语言风格给出医生核心问题的回答。注意：回答涉及提供的患者信息时，需与患者详细信息一致；回答未涉及提供的患者信息时，内容需与患者详细信息逻辑合理不冲突\n"
                # system_content += "5. 人格特征润色：对中性回答按照人格定义进行调整润色，符合人格的定义、表现、约束。\n\n"

                # 生成要求（明确输出边界，结合人格约束）
                system_content += "【输出规则】\n"
                #system_content += "- 依次生成每一步思考的结果，第5步思考结果后添加</think>字符。最后生成最终的患者回应，患者回应紧跟在</think>字符后面，绝对不包含前面的思考步骤、任务说明、信息列表等内容\n"
                system_content += "- 仅生成最终患者回应，绝对不包含思考步骤、任务说明、信息列表等内容\n"

                # 基础任务说明
                system_content += "【核心要求】\n"
                system_content += "- 所有回应必须严格匹配5个人格维度的定义，禁止出现与人格矛盾的表述（例如‘病史回忆能力低’的患者，不能准确说出1个月前的用药细节）！\n\n"

                #system_content += "- 回应必须体现人格特征（例如：‘病史回忆能力低’的患者，回答‘咳嗽多久’时可写‘大概一周吧，记不太清’，不能写‘刚好5天’）\n"
                system_content += "- 信息必须与【患者基本信息】【门诊病历】一致\n"
                #system_content += "- 语言表达匹配‘语言表达能力’维度（例如‘能力低’的患者，只能说‘咳得厉害’，不能说‘我最近咳嗽症状比较严重，尤其是早上起床后’）\n"
                system_content += "- 避免书面语，用日常口语（如不说‘症状持续时间为5天’，说‘咳了5天了’）\n"


                # 添加患者基本信息（结构化分区，方便思考时调用）
                if patient_info_dict:
                    system_content += "【患者基本信息】\n"
                    for key, value in patient_info_dict.items():
                        system_content += f"- {key}：{value}\n"
                    system_content += "\n"  # 换行分隔，提升可读性

                # 添加人格定义描述
                if target_personality:
                    personality_desc = self.prompt_generator.get_personality_description(target_personality)
                    system_content += f"\n{personality_desc}\n"

                # 添加门诊病历信息
                if data.get("门诊病历"):
                    system_content += "【门诊病历】\n"
                    medical_record = data["门诊病历"]
                    if isinstance(medical_record, dict):
                        for key, value in medical_record.items():
                            system_content += f"- {key}：{value}\n"
                    else:
                        system_content += f"{medical_record}\n"
                    system_content += "\n"

                 

                # 添加对话历史
                if data["context_history"]:
                    system_content += "【对话历史】\n"
                    for i, turn in enumerate(data["context_history"]):
                        system_content += f"轮次 {i+1}：\n"
                        if turn.get("医生"):
                            system_content += f"  医生：{turn['医生']}\n"
                        if turn.get("患者"):
                            system_content += f"  患者：{turn['患者']}\n"
                    system_content += "\n"


                # 转换为模型输入格式
                model_input = []

                # 添加system消息（包含人格化思维链引导）
                model_input.append({
                    "role": "system", 
                    "content": system_content
                })

                # 添加当前医生问题作为user消息
                model_input.append({
                    "role": "user", 
                    "content": data["doctor_question"]
                })
                
                # 转换为文本格式
                
                # 打印完整提示词用于检查
                logger.info(f"完整提示词: {model_input}")
                
                # 调用模型生成回答（使用单条生成，而不是批量生成）
                raw_response = self._call_model_generate(model_input)
                
                # 打印模型原生生成内容用于检查
                logger.info(f"模型生成内容: {raw_response}")
            
                responses.append(raw_response)
                #responses.append(cleaned_response)
                
        except Exception as e:
            logger.error(f"批量生成回答失败: {str(e)}")
            # 如果出错，返回已生成的回答，并为剩余请求生成默认回答
            remaining_count = len(batch_data) - len(responses)
            if remaining_count > 0:
                responses.extend(["（生成回答时发生错误）"] * remaining_count)
        
        # 记录处理时间
        logger.info(f"批量处理完成，总耗时: {time.time() - start_time:.2f}秒")
        return responses
    
    def _generate_with_interaction_matrix(self, batch_data: List[Dict[str, Any]]) -> List[str]:
        """
        使用交互矩阵方法生成回答
        
        Args:
            batch_data: 批量数据
            
        Returns:
            生成的回答列表
        """
        responses = []
        
        for i, data in enumerate(batch_data):
            logger.info(f"使用交互矩阵处理第 {i+1}/{len(batch_data)} 个请求")
            
            try:
                # 解析人格信息
                target_personality = self._parse_personality_info(data["人格"])
                personality = target_personality.get('性格', '')
                emotion = target_personality.get('情绪状态', '')
                
                # 解析患者基本信息
                patient_info_dict = self._parse_patient_info(data["基础信息"])
                
                # 生成交互矩阵增强的提示词
                system_content = self._generate_interaction_matrix_prompt(
                    target_personality, patient_info_dict, data
                )
                #logger.info(f"交互矩阵增强的提示词: {system_content}")
                # 构建模型输入
                model_input = [
                    {"role": "system", "content": system_content},
                    {"role": "user", "content": data["doctor_question"]}
                ]
                
                # 调用模型生成回答
                response = self.generate_with_messages(model_input)
                responses.append(response)
                
            except Exception as e:
                logger.error(f"交互矩阵生成第{i+1}个回答失败: {str(e)}")
                responses.append("（交互矩阵生成回答时发生错误）")
        
        return responses
    
    def _generate_interaction_matrix_prompt(self, personality_info: Dict[str, str], 
                                          patient_info_dict: Dict[str, str], 
                                          data: Dict[str, Any]) -> str:
        """
        生成交互矩阵增强的提示词
        
        Args:
            personality_info: 人格信息
            patient_info_dict: 患者基本信息
            data: 完整数据
            
        Returns:
            生成的提示词
        """
        prompt_parts = []
        
        # 任务指令
        prompt_parts.append("你需要扮演一个具有特定人格特征和情绪状态的患者，与医生进行门诊对话。")
        prompt_parts.append("【输出要求】：仅生成最终患者的回应内容，绝对不包含本system指令中的任何内容")
        prompt_parts.append("【格式要求】：直接输出患者的回应内容，不要添加任何前缀或标识（如'患者：'等）")
        
        # 添加医疗信息保留要求
        medical_preservation = """
关键医疗信息保留要求（必须遵守）：
1. 必须保留所有症状描述：如疼痛部位、程度、持续时间等
2. 必须保留所有医疗时间信息：如发病时间、用药时长等  
3. 必须保留所有药物信息：药名、剂量、用法
4. 必须保留所有检查信息：检查类型、结果、时间
5. 必须保留所有数字信息：年龄、剂量、时间数量等
"""
        prompt_parts.append(medical_preservation)
        
        # 患者人格特征和情绪状态
        prompt_parts.append("\n患者人格特征和情绪状态：")
        prompt_parts.append(json.dumps(personality_info, ensure_ascii=False))
        
        # 添加能力维度详细说明
        prompt_parts.append("\n能力维度说明（必须严格遵循）：")
        
        # 医学理解力说明
        health_literacy = personality_info.get('医学理解力', '')
        if health_literacy:
            if health_literacy == '高':
                prompt_parts.append("- 医学理解力（高）：定义：具备一定医学常识。表现：能理解并使用常见疾病相关的医学术语，与医生沟通顺畅。约束：不要过度专业化，保持患者身份的自然性。")
            elif health_literacy == '低':
                prompt_parts.append("- 医学理解力（低）：定义：缺乏医学相关知识。表现：只能理解和使用日常生活中常见的医学词汇，对复杂术语表现为困惑或需要解释。约束：所有医学术语需要医生解释或用简单语言转述。")
        
        # 病史回忆能力说明
        recall_accuracy = personality_info.get('病史回忆能力', '')
        if recall_accuracy:
            if recall_accuracy == '高':
                prompt_parts.append("- 病史回忆能力（高）：定义：能准确回忆病史。表现：清楚描述时间、药名、症状等关键信息。约束：回答具体，不需要反复确认。")
            elif recall_accuracy == '低':
                prompt_parts.append("- 病史回忆能力（低）：定义：病史回忆能力差。表现：忘记重要细节，回答含糊或前后不一致。约束：{\"回答比例\": {\"完整或部分回答轮次\": \"70%-80%\", \"遗忘或含糊回答轮次\": \"20%-30%\"}, \"遗忘范围\": [\"症状或疾病发生时间\", \"用药剂量、次数、疗程\", \"久远的既往史\", \"家族史等次要信息\"], \"禁止遗忘\": [\"主要主诉（就诊原因）\", \"当前最明显症状\"]}, 表现形式示例: [\"模糊回答：'大概一周左右吧，我记得不是很清楚。'\", \"含糊回答：'好像吃了几天药，具体不太记得了。'\", \"遗忘回答：'这个我真想不起来了。'\", \"前后矛盾：'之前说三天，后来又说一周。'\"]")
        
        # 语言表达能力说明
        language_fluency = personality_info.get('语言表达能力', '')
        if language_fluency:
            if language_fluency == '高':
                prompt_parts.append("- 语言表达能力（高）：定义：表达流利、条理清晰。表现：用词准确，句子完整，逻辑性强。约束：保持自然口语化，而非书面语。")
            elif language_fluency == '中':
                prompt_parts.append("- 语言表达能力（中）：定义：日常表达能力正常。表现：能使用常见词汇形成连贯句子，偶尔语法错误或缺少衔接词。约束：避免复杂抽象的词汇或过长句子。")
            elif language_fluency == '低':
                prompt_parts.append("- 语言表达能力（低）：定义：语言表达有限。表现：常用短语回答，句子不完整，语法错误较多。约束：不得使用复杂词汇或长句。")
        
        # 添加增强要求（三层控制约束）
        enhancement_requirements = """
对话增强要求（严格约束）：
【第一层：记忆一致性层 - 逻辑稳定控制】
- 语义核心保留：请务必保留原患者发言中的所有事实性医疗信息；
- 逻辑连贯：患者的回答必须与医生的提问保持逻辑关联，确保对话上下文连贯，避免答非所问；
- 轮次一致性：保证对话内容不自相矛盾，符合记忆约束规则；
- 遗忘比例控制：严格遵守病史回忆能力中关于遗忘范围和比例的规定。

【第二层：交互矩阵层 - 态度取向控制】
- 人格体现：在对话中自然且明显地体现目标人格特征和情绪状态；
- 场景适配：根据对话内容判断场景，并按照交互矩阵中对应场景的规则进行调整；
- 决定对话的'态度取向'（主动/被动、接受/质疑）；
- 怀疑人格特殊限制：对于怀疑人格患者，禁止连续多轮怀疑，并且总怀疑轮数不超过对话总轮数的40%。

【第三层：表达约束层 - 表现形式控制】
- 医学理解力：严格按照指定的医学理解力水平调整患者对医学术语的理解和使用能力；
- 病史回忆能力：严格按照指定的病史回忆能力水平调整患者回忆病史细节的准确性；
- 语言表达能力：严格按照指定的语言表达能力水平调整患者的语言表达流畅度和完整性；
- 决定对话的'表现形式'（语言难度、清晰度、完整性）。

【情绪强度控制】
- 出现场景：根据对话内容和患者当前情绪状态自然体现（如焦虑情绪在等待诊断结果时体现）。
- 表达边界：
  禁止：极端情绪宣泄、人身攻击、拒绝合理建议。

【通用要求】
- 表达风格：口语化表达，符合真实医患对话场景；
"""
        prompt_parts.append(enhancement_requirements)
        
        # 添加交互矩阵规则
        prompt_parts.append("\n特别说明：交互矩阵规则中括号里的内容只是示例，禁止重复照搬示例内的语句。")
        prompt_parts.append("\n交互矩阵规则（必须严格遵循）：")
        
        personality = personality_info.get('性格', '')
        emotion = personality_info.get('情绪状态', '')
        
        if personality and emotion and self.interaction_matrix and personality in self.interaction_matrix['rules'] and emotion in self.interaction_matrix['rules'][personality]:
            rules_for_personality_emotion = self.interaction_matrix['rules'][personality][emotion]
            rules_text = []
            for scene, behavior in rules_for_personality_emotion.items():
                # 处理结构化的行为配置
                if isinstance(behavior, dict):
                    # 对于结构化配置，提取方向、轻度、中度和禁忌信息
                    direction = behavior.get('方向', '')
                    mild = behavior.get('轻度', '')
                    moderate = behavior.get('中度', '')
                    taboo = behavior.get('禁忌', '')
                    
                    behavior_text = []
                    if direction:
                        behavior_text.append(f"方向：{direction}")
                    if mild:
                        behavior_text.append(f"轻度情绪：{mild}")
                    if moderate:
                        behavior_text.append(f"中度情绪：{moderate}")
                    if taboo:
                        behavior_text.append(f"禁忌：{taboo}")
                    
                    rules_text.append(f"- {scene}：{'; '.join(behavior_text)}")
                else:
                    # 对于简单文本配置，保持原格式
                    rules_text.append(f"- {scene}：{behavior}")
            prompt_parts.append("\n".join(rules_text))
        else:
            # 如果没有找到匹配的规则，使用通用指导
            prompt_parts.append("- 请确保患者的语言风格、用词习惯和态度与指定的性格和情绪状态高度一致")
            prompt_parts.append("- 在不同场景下（如问诊初期、提及检查、告知治疗风险、医患分歧）表现出不同的反应模式")
            prompt_parts.append("- 保持对话的自然流畅，避免过于机械或夸张")
        
        # 添加患者基本信息
        if patient_info_dict:
            prompt_parts.append("\n【患者基本信息】")
            for key, value in patient_info_dict.items():
                prompt_parts.append(f"- {key}：{value}")
            prompt_parts.append("")
        
        # 添加门诊病历信息
        if data.get("门诊病历"):
            prompt_parts.append("【门诊病历】")
            medical_record = data["门诊病历"]
            if isinstance(medical_record, dict):
                for key, value in medical_record.items():
                    prompt_parts.append(f"- {key}：{value}")
            else:
                prompt_parts.append(f"{medical_record}")
            prompt_parts.append("")
        
        # 添加对话历史
        if data["context_history"]:
            prompt_parts.append("【对话历史】")
            for i, turn in enumerate(data["context_history"]):
                prompt_parts.append(f"轮次 {i+1}：")
                if turn.get("医生"):
                    prompt_parts.append(f"  医生：{turn['医生']}")
                if turn.get("患者"):
                    prompt_parts.append(f"  患者：{turn['患者']}")
            prompt_parts.append("")
        
        return "\n".join(prompt_parts)

